初识 TypeScript

基础类型

  • any 任何类型
  • number 数字类型
  • string 字符串
  • boolean 布尔值
  • Array 数组 各类型元素相同 例如 any[] 或 Array
  • Tuple 元组 各类型元素不同
  • enum 枚举
  • void 函数无返回值
  • null 空
  • undefined 未定义
  • never 永远不存在值的类型

自定义类型

  • 自定义类型 type xxx = number | string
  • 字符串字面量类型 type xxx = ‘click’ | ‘scroll’ …

变量声明

1
2
3
var [变量名] : [类型] = 值;
var uname: string = 'jack'
var uname: string;

类型断言(type assertion)

类型断言可以手动指定一个值得类型,即允许变量从一种类型更改为另一种类型.

1
2
3
4
5
6
7
8
9
10
// <类型>值
// 或: 值 as 类型
// JSX 中使用 <type> 的断言语法时,这会与 JSX 的语法存在歧义,建议使用 as
interface Foo {
bar: number;
bas: string;
}
const foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hello';

类型推断

当类型没有指定时,TypeScript 编译器会利用类型推断来推断类型.

如果由于缺乏声明而不能推断出类型,那么它的类型被视作默认的动态 any 类型.

1
2
var num = 2;//类型推断为 number
num = '12';// 编译错误

变量作用域

  • 全局作用域(全局变量)
  • 类作用域(类变量,静态变量)
  • 局部作用域(局部变量)
1
2
3
4
5
6
7
8
9
10
11
12
var global_num = 12 // 全局变量
class Numbers {
num_val = 13; // 类变量
static sval = 10; // 静态变量
storeNum(): void{
var local_num = 14; // 局部变量
}
}
console.log('全局变量为'+global_num)
console.log(Numbers.sval)//静态变量
var obj = new Numbers()
console.log('类变量'+obj.num_val)

函数

函数返回值

1
2
3
4
// 函数定义
function greet():string{
return 'helloworld'
}

函数参数

1
2
3
function add (x:number,y:number): number{
return x + y
}

可选参数

1
2
3
4
5
6
7
8
9
10
function buildName(firstName:string,lastName ?:string){
if(lastName){
return firstName + ' ' + lastName
}else{
return firstName
}
}
let result1 = buildName('bob') // 'bob'
let result2 = buildName('bob','jack','adams') // 错误,参数过多
let result2 = buildName('bob','jack') // bob jack

默认参数

1
2
3
function caculate(price:number,rate:number = 0.50){
return price * rate
}

剩余参数

1
2
3
function buildName(firstName: string, ...restName:string[]){
return firstName + ' ' + restName.join(' ')
}

lambda 箭头函数

1
var foo = (x:number) => 10 + x

联合类型

可以通过管道符|将变量设置多种类型.赋值时可以根据设置的类型来赋值,也可以将联合类型作为函数参数使用.

1
2
3
4
5
var val:string|number
val = 12
val = 'jack'
// 数组联合声明
var arr:number[] | string[]

交叉类型

你可以通过&交叉类型从两个对象中创建一个新对象,新对象会拥有着两个对象所有的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function extend<T, U>(first: T, second: U): T & U {
const result = <T & U>{};
for (let id in first) {
(<T>result)[id] = first[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<U>result)[id] = second[id];
}
}

return result;
}

const x = extend({ a: 'hello' }, { b: 42 });

// 现在 x 拥有了 a 属性与 b 属性
const a = x.a;
const b = x.b;

TypeScript 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
interface IPerson{
firstName: string,
// 只读属性
readonly lastName:string,
sayHi: ()=>string,
// 可选属性
age?:number,
//任意属性 其类型必须是其他属性类型的子集, any,null...
[propName:string]: any
}
var customer: IPerson = {
firstName: 'Tom',
lastName: 'Hanks',
sayHi: ():string => {
return 'hi'
}
}
// 函数也可以使用 interface
interface SearchFunc{
(source: string, subString:string):boolean;
}
let mySearch: SearchFunc;
mySearch = function(source:string,subString:string){
return source.search(subString) !== -1;
}
// 可索引类型(定义索引类型)
interface StringArray{
[index:number]: string
}
let MyArray : StringArray;
MyArray = ['初春令月','气淑风和']
console.log(MyArray[1])
// 接口也可以继承接口
interface Shape{
color: string
}
interface PenStroke{
penWidth:nubmer
}
interface Square extends Shape,PenStroke{
sideLength:number
}
let s = <Square>{};
s.color="blue"
s.penWidth =100
s.sideLength = 10
// 接口还可以继承类
class Point{
x:number;
y:number;
}
interface Point3d extend Point{
z:number;
}
let point3d : Point3d{
x:1,
y:2,
z:3
}
内联类型注解

内联类型能为你快速的提供一个类型注解。它可以帮助你省去为类型起名的麻烦,如果多次使用相同的内联注解时,可以考虑把它重构为一个接口.

1
2
3
4
5
6
7
8
9
let name: {
first: string;
second: string;
};

name = {
first: 'John',
second: 'Doe'
};

TypeScript 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Car{
//字段
engine: string;
_name: string;
// 构造函数
constructor(engine:string){
this.engine = engine
}
// 储存器
get name():string{
return this._name
}
set name(value:string){
this._name = value
}
// 方法
disp():void{
console.log("发动机为:" + this.engine)
}
}
class Feature extends Car{
static name: string; // 静态变量或静态方法,不需要实例化,直接通过类调用
constructor(){
super() // 调用父类构造函数和方法
}
// private 私有,只能在类中访问
// protected 受保护,只能在自身以及子类父类访问
// public(默认) 公有,可以再任何地方被访问
run():void{
console.log('品牌是:'+Feature.name)
}
}
// 创建一个实例
var obj = new Car('XXSY1')
// 抽象类(不允许实例化,但可以被继承,内部方法也可以抽象化)
abstrct class xxx{...}
// 接口 interface 可以对类的一部分进行抽象.implements(实现)是一个重要概念.不同类之间共有的特性提取为接口用 implements 实现.一个类 可以实现多个接口.
interface Light{
lightOn() // 开灯
lightOff() // 关灯
}
class Car implements Light{
lightOn() // 开灯
lightOff() // 关灯
}

模块

TypeScript 文件模块支持commonjs, amd, es modules, others,你可以根据不同的 module 选项来把 TypeScript 编译成不同的 JavaScript 模块类型.

1
2
3
4
5
6
// 文件名: SomeInterface.ts
export interface SomeInterface{
// ...
}
// 引用
import {SomeInterfaceRef} from './SomeInterface'

泛型

泛型是在定义函数,接口或类时,不预先指定具体类型,而是在使用时再指定的一种特性.

使用泛型来创建可重用的组件,一个组件可以支持多种数据类型.这样用户就可以以自己的数据类型来使用组件.

1
2
3
4
5
function Hello<T>(arg:T):T{
return arg
}
let output = Hello<string>('helloworld')//调用时指定类型
let output2 = Hello('helloworld') // 或不指定,让类型推论自动推算

泛型约束是在函数内部使用泛型变量时,由于事先不知道它是哪种类型,所以不能随意的操作它的属性和方法,比如 length 属性.

1
2
3
4
5
6
7
8
interface Lengthwise {
length: number
}
function loggin<T extends Lengthwise>(arg:T):T{
console.log(arg.length)
return arg
}
// 泛型通过接口约束了必须有 length 方法,如果没有编译阶段会报错

接口可以定义函数形状,那么有泛型的接口也可以定义函数形状

1
2
3
interface CreateArrayFunc<T>{
(length:number,value:T): Array<T>
}

泛型类(当实际参数也无法推测出类型时,我们还可以给泛型指定默认参数)

1
2
3
4
class GenericNumber<T=string> {
zeroValue: T;
add: (x:T,y:T) => T;
}

声明合并

如果声明了两个以上的 相同名字的函数,接口或类,那么它们会合并成一个类型.

函数合并

我们可以使用函数重载定义多个函数类型

1
2
3
4
5
6
7
8
9
function reverse(x:number):number;
function reverse(x:string):string;
function reverse(x:number | string):number | string{
if(typeof x === 'number'){
return Number(x.toString().split('').reverse().join(''))
}else if(typeof x === 'string'){
return x.split('').reverse().join('')
}
}
接口合并

接口合并的类型要一致,否则会报错,接口中方法合并和函数合并一样.类合并和接口合并规则一样

1
2
3
4
5
6
7
8
9
10
11
interface Alarm {
price: number;
}
interface Alarm {
weight: number;
}
// 合并后相当于
interface Alarm {
price: number;
weight: number;
}

声明文件

1
2
3
4
5
6
7
8
9
10
11
12
declare var jQuery: (selector: string) => any
jQuery('#foo')
// 我们通常把声明放到一个文件中,比如 jquery.d.ts
// 推荐使用@types 统一管理第三方库的声明文件
// 如果第三方没有提供,需要我们自己书写声明文件
declare var // 全局变量
declare function // 全局函数
declare class // 全局类
declare enum // 全局枚举
declare namespace // 全局对象(含子属性)
interface // 全局接口
type // 全局类型

命名空间

TypeScript 提供了 namespace 关键字用来在确保创建的变量不会泄漏至全局变量中.

1
2
3
4
5
6
7
8
9
10
11
12
namespace Utility {
export function log(msg) {
console.log(msg);
}
export function error(msg) {
console.log(msg);
}
}

// usage
Utility.log('Call me');
Utility.error('maybe');

namespace 关键字通过 TypeScript 编译后,与我们看到的 JavaScript 代码一样:

1
2
3
(function (Utility) {
// 添加属性至 Utility
})(Utility || Utility = {});